package com.fuego.creditsystemuser.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fuego.creditsystemuser.mapper.UserMapper;
import com.fuego.creditsystemuser.service.IUserService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.example.dto.LoginFormDTO;
import org.example.dto.Result;
import org.example.dto.UserDTO;
import org.example.entity.User;
import org.example.utils.RegexUtils;
import org.example.utils.UserHolder;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static org.example.utils.RedisConstants.*;
import static org.example.utils.SystemConstants.USER_NICK_NAME_PREFIX;


@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Override
    @Transactional
    public Result update(User user) {
        if (user.getId() == null) {
            return Result.fail("用户ID不能为空");
        }
        boolean updated = updateById(user);
        return updated ? Result.ok() : Result.fail("更新失败");
    }

    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Override
    public Result sendCode(String phone) {
        //1校验手机号
        if(RegexUtils.isPhoneInvalid(phone))
        {
            //2如果不符合返回错误信息
            return Result.fail("手机号格式错误！");
        }
        //3符合生成验证码
        String code=RandomUtil.randomNumbers(6);

        //4保存验证码到redis//验证码两分钟过期
        stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY+phone,code,LOGIN_CODE_TTL, TimeUnit.MINUTES);
        //5发送验证码
        log.debug("发送短信验证码成功，验证码：{}",code);
        //返回OK
        return Result.ok();
    }
    @Override
    public Result login(LoginFormDTO loginForm) {
        //1校验手机号
        String phone=loginForm.getPhone();
        if(RegexUtils.isPhoneInvalid(phone))
        {
            return Result.fail("手机号格式错误！");
        }
        //3从redis获取验证码并校验
        String cacheCode=stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY+phone);
        String code=loginForm.getCode();
        if(cacheCode==null || !cacheCode.equals(code))
        {
            //3不一致报错
            return Result.fail("验证码错误");
        }

        //4一致，根据手机号查询用户
        User user=query().eq("phone",phone).one();
        //5判断用户是否存在
        if(user==null)
        {
            //6不存在，创建用户并保存
            user=createUserWithPhone(phone);
        }


        //TODO 7保存用户信息到redis中
        // 7.1随机生成token ，作为登录令牌
        String token= UUID.randomUUID().toString(true);//true代表简单，无下划线
        //TODO 7.2将user对象转为hash存储
        UserDTO userDTO=BeanUtil.copyProperties(user,UserDTO.class);
        Map<String, Object> userMap=BeanUtil.beanToMap(userDTO,new HashMap<>(),
                CopyOptions.create().setIgnoreNullValue(true)
                        .setFieldValueEditor((fieldname,fieldValue)->fieldValue.toString()));
        //TODO 7.3存储
        String tokenKey="login:token:"+token;
        stringRedisTemplate.opsForHash().putAll(LOGIN_USER_KEY+token,userMap);
        //TODO 7.4设置token有效期

        stringRedisTemplate.expire(tokenKey,LOGIN_USER_TTL,TimeUnit.MINUTES);

        //TODO 返回token
        return Result.ok(token);
    }

    public Long getIdByToken(HttpServletRequest request){
        String token = request.getHeader("Authorization");
        if (token == null || token.isEmpty()) {
            log.warn("No token provided");
            return 0L;
        }
        if (token.startsWith("Bearer ")) {
            token = token.substring(7); // 移除 "Bearer " 前缀
        }

        // 2. 从 Redis 获取用户信息
        String tokenKey = "login:token:" + token;
        Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(tokenKey);
        if (userMap == null || userMap.isEmpty()) {
            log.warn("Invalid or expired token {}", token);
            return -1L;
        }

        // 3. 构造 User 对象
        Long userId=Long.parseLong(userMap.get("id").toString());
        return userId;
    }
    @Override
    public Result sign() {
        //1获取当前登录用户
        Long userId = UserHolder.getUser().getId();
//        2获取日期
        LocalDateTime now=LocalDateTime.now();
//        3拼接key
        String keySuffix=now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        String key=USER_SIGN_KEY+userId+keySuffix;
//        4获取今天是本月的第几天
        int dayOfMonth=now.getDayOfMonth();

//        5写入Redis SETBIT key offset 1
        stringRedisTemplate.opsForValue().setBit(key,dayOfMonth-1,true);
        return Result.ok();
    }

    @Override
    public Result signCount() {
        //1获取当前登录用户
        Long userId = UserHolder.getUser().getId();
//        2获取日期
        LocalDateTime now=LocalDateTime.now();
//        3拼接key
        String keySuffix=now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        String key=USER_SIGN_KEY+userId+keySuffix;
//        4获取今天是本月的第几天
        int dayOfMonth=now.getDayOfMonth();
        //获取本月截止今天为止的所有签到记录，返回的是一个十进制的数字
        List<Long> result = stringRedisTemplate.opsForValue().bitField(
                key,
                BitFieldSubCommands.create()
                        .get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0)
        );
        if(result==null ||result.isEmpty()){
            return Result.ok(0);
        }
        Long num = result.get(0);
        if(num==null ||num==0){
            return Result.ok(0);
        }
//      循环遍历
        int count=0;
        while(true){
//        让这个数字与1做与运算，得到数字的最后一个bit位//        判断这个bit位是否为0
            if ((num & 1)==0){
                //        如果为0，说明未签到，结束
                break;
            }else{
                //        如果不为零，说明已经签到，计数器+1
                count++;
            }
//        把数字右移一位，抛弃最后一个bit位，继续下一个bit位
            num>>>=1;
        }
        return Result.ok(count);
    }

    private User createUserWithPhone(String phone) {
        User user=new User();
        user.setPhone(phone);
        user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));
        save(user);//mybatis plus方法
        return user;
    }
}
